package fudan.se.project.util.random;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;

@Component
@Slf4j
public class RandomUtils {

    private Random random;


    public RandomUtils() {
        try {
            random = SecureRandom.getInstanceStrong();
        } catch (NoSuchAlgorithmException e) {
            log.error("no cryptographic algorithm available in the environment");
        }
    }

    public Collection<Integer> generateDistinctRandoms(int count, int bound) {
        if (count <= 0 || bound < 0 || random == null) {
            return new ArrayList<>(0);
        }
        if (count > bound) {
            return new ArrayList<>(0);
        }

        ArrayList<Integer> randoms = new ArrayList<>(count);
        if (count == bound) {
            addNaturalNumbers(randoms, count);
        } else if (2 * count <= bound) {
            addRandomsToList(count, bound, randoms);
        } else {
            addNaturalNumbers(randoms, bound);
            removeRandomsFromList(bound - count, bound, randoms);
        }

        return randoms;
    }

    private void addNaturalNumbers(List<Integer> list, int bound) {
        for (int i = 0; i < bound; i++) {
            list.add(i);
        }
    }

    private void addRandomsToList(int count, int bound, List<Integer> list) {
        for (int i = 0; i < count; i++) {
            int integer = random.nextInt(bound);
            while (list.contains((integer))) {
                integer = random.nextInt(bound);
            }
            list.add(integer);
        }
    }

    private void removeRandomsFromList(int count, int bound, List<Integer> list) {
        int index = 0;
        for (int i = 0; i < count; i++) {
            int integer = random.nextInt(bound);
            while (-1 == (index = list.indexOf(integer))) {
                integer = random.nextInt(bound);
            }
            list.remove(index);
        }
    }
}
